package com.yb0os1.ComplexJDKProxy;

import com.yb0os1.SimpleJDKProxy.Compiler;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

public class MyProxy {
    //这里我们实现单个接口的代理
    private static final AtomicInteger count = new AtomicInteger(0);
    private static final String packName = "com.yb0os1.ComplexJDKProxy";

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?> classInfo,
                                          MyInvocationHandler h) throws Exception {
        String  className = getProxyClassName(classInfo.getName());
        //1、写代理类的java文件
        File proxyJavaFile = writeProxyJavaFile(className,classInfo);
        //2、编译java文件为class文件
        Compiler.compile(proxyJavaFile);//编译成class文件
        //3、加载class文件
        Class<?> proxyClass = loader.loadClass(packName + "." + className);
        // 4、创建对象并返回
        Object proxy = newInstance(proxyClass,h);
        return proxy;
    }

    private static Object newInstance(Class<?> proxyClass, MyInvocationHandler h) throws Exception {
        Constructor<?> constructor = proxyClass.getConstructor(MyInvocationHandler.class);
        return constructor.newInstance(h);
    }

    private static File writeProxyJavaFile(String className,Class<?> classInfo) throws IOException {
        //获取方法
        Method[] methods = classInfo.getMethods();
        String interfaceName = classInfo.getName();
        StringBuilder classContext = new StringBuilder();
        String proxyMethod = getProxyMethod(interfaceName, methods);

        classContext.append("package ").append(packName).append(";\n")
                .append("import java.lang.reflect.Method;\n")
                .append("import com.yb0os1.ComplexJDKProxy.MyInvocationHandler;\n")
                .append("public class ").append(className).append(" implements ").append(interfaceName).append("{\n")
                .append("private MyInvocationHandler handler;public ").append(className).append("(MyInvocationHandler handler) {this.handler = handler;}")
                .append(proxyMethod)
                .append("}");
        File file = new File(className + ".java");
        Files.writeString(file.toPath(), classContext.toString());
        return file;
    }

    private static String getProxyClassName(String className) {
        return  className.substring(className.lastIndexOf('.') + 1)+"Proxy$" + count.getAndIncrement();
    }

    // TODO 这里代理类的方法我们只实现了无返回值无参数的方法
    private static String getProxyMethod(String interfaceName, Method[] methods) {
        StringBuilder methodContext = new StringBuilder();
        for(Method method : methods){
            String parameters = String.join(", ", Arrays.stream(method.getParameters())
                    .map(p -> p.getType().getSimpleName() + " " + p.getName())
                    .toList());
            System.out.println();
            methodContext.append("public ").append(method.getReturnType().getName()).append(" ").append(method.getName()).append("(").append(parameters).append("){");
            methodContext.append("try{this.handler.invoke(this,").append(interfaceName)
                    .append(".class.getMethod(\"").append(method.getName()).append("\")").append(",")
                    .append("null").append(");}catch(Throwable e){}");
            methodContext.append("}");
        }
        System.out.println(methodContext);
        return methodContext.toString();
    }
}
